namespace SimpleWebServices.Web.Mvc
{
    using System;
    using System.Configuration;
    using System.IO;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Xml;
    using Moq;
    using SimpleWebServices;
    using SimpleWebServices.Configuration;
    using SimpleWebServices.Data;
    using SimpleWebServices.Security.Cryptography;
    using SimpleWebServices.Web.Routing;
    using Xunit;

    public sealed class FileControllerFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<FileController>()
                .DerivesFrom<Controller>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .IsDecoratedWith<InternalServerErrorAttribute>()
                .Allow("GET, HEAD, OPTIONS")
                .IsDecoratedWith<ContentMD5Attribute>()
                .Implements<IRegisterRoutes>()
                .Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new FileController());
        }

        [Fact]
        public void op_Register_RouteCollectionNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new FileController() as IRegisterRoutes).Register(null));
        }

        [Fact]
        public void op_Register_RouteCollection()
        {
            RouteCollection routes = new RouteCollection();
            (new FileController() as IRegisterRoutes).Register(routes);

            Assert.IsType<FileRoute>(routes[0]);
        }

        [Fact]
        public void op_Representation()
        {
            FileConfiguration.Settings().Clear();

            try
            {
                FileInfo file = new FileInfo(Environment.GetEnvironmentVariable("Temp") + @"\" + Token.New());
                using (file.Create())
                {
                }

                FileConfiguration.Settings()
                    .Add(("<file content-type='text/plain' path='/example.txt' location='" + file.FullName + "' />")
                    .Deserialize<FileSetting>());

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.Path).Returns("/example.txt").Verifiable();
                mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.NoCache)).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FileStreamResult result = new FileController().Representation() as FileStreamResult;

                Assert.NotNull(result);

                mock.VerifyAll();
            }
            finally
            {
                ConfigurationManager.RefreshSection("simplewebservices/files");
            }
        }

        [Fact]
        public void op_Representation_whenFileNotDefined()
        {
            FileConfiguration.Settings().Clear();

            try
            {
                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.Path).Returns("/example.txt").Verifiable();
                HttpContextFactory.Mock = mock.Object;

                NotFoundResult result = new FileController().Representation() as NotFoundResult;

                Assert.NotNull(result);

                mock.VerifyAll();
            }
            finally
            {
                ConfigurationManager.RefreshSection("simplewebservices/files");
            }
        }

        [Fact]
        public void op_Representation_whenFileNotFound()
        {
            FileConfiguration.Settings().Clear();

            try
            {
                FileInfo file = new FileInfo(Environment.GetEnvironmentVariable("Temp") + @"\" + Token.New());

                FileConfiguration.Settings()
                    .Add(("<file content-type='text/plain' path='/example.txt' location='" + file.FullName + "' />")
                    .Deserialize<FileSetting>());

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.Path).Returns("/example.txt").Verifiable();
                HttpContextFactory.Mock = mock.Object;

                NotFoundResult result = new FileController().Representation() as NotFoundResult;

                Assert.NotNull(result);

                mock.VerifyAll();
            }
            finally
            {
                ConfigurationManager.RefreshSection("simplewebservices/files");
            }
        }

        [Fact]
        public void op_Representation_whenPublicEtag()
        {
            FileConfiguration.Settings().Clear();

            try
            {
                FileInfo file = new FileInfo(Environment.GetEnvironmentVariable("Temp") + @"\" + Token.New());
                using (file.Create())
                {
                }

                FileConfiguration.Settings()
                    .Add(("<file cache-condition='etag' cache-control='public' content-type='text/plain' path='/example.txt' location='" + file.FullName + "' />")
                    .Deserialize<FileSetting>());

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.Path).Returns("/example.txt").Verifiable();
                mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
                mock.Setup(x => x.Response.Cache.SetETag(It.Is<string>(etag => etag == MD5Hash.Compute(file)))).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FileStreamResult result = new FileController().Representation() as FileStreamResult;

                Assert.NotNull(result);

                mock.VerifyAll();
            }
            finally
            {
                ConfigurationManager.RefreshSection("simplewebservices/files");
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_Representation_whenPublicExpires()
        {
            FileConfiguration.Settings().Clear();

            try
            {
                DateTimeFactory.Mock = DateTime.UtcNow;
                TimeSpan duration = XmlConvert.ToTimeSpan("P1Y");

                FileInfo file = new FileInfo(Environment.GetEnvironmentVariable("Temp") + @"\" + Token.New());
                using (file.Create())
                {
                }

                FileConfiguration.Settings()
                    .Add(("<file cache-condition='expires' duration='P1Y' cache-control='public' content-type='text/plain' path='/example.txt' location='" + file.FullName + "' />")
                    .Deserialize<FileSetting>());

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.Path).Returns("/example.txt").Verifiable();
                mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
                mock.Setup(x => x.Response.Cache.SetExpires(It.Is<DateTime>(date => date == DateTimeFactory.UtcNow.Add(duration)))).Verifiable();
                mock.Setup(x => x.Response.Cache.SetMaxAge(It.Is<TimeSpan>(delta => delta == duration))).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FileStreamResult result = new FileController().Representation() as FileStreamResult;

                Assert.NotNull(result);

                mock.VerifyAll();
            }
            finally
            {
                ConfigurationManager.RefreshSection("simplewebservices/files");
                HttpContextFactory.Reset();
                DateTimeFactory.Reset();
            }
        }

        [Fact]
        public void op_Representation_whenPublicLastModified()
        {
            FileConfiguration.Settings().Clear();

            try
            {
                FileInfo file = new FileInfo(Environment.GetEnvironmentVariable("Temp") + @"\" + Token.New());
                using (file.Create())
                {
                }

                FileConfiguration.Settings()
                    .Add(("<file cache-condition='last-modified' cache-control='public' content-type='text/plain' path='/example.txt' location='" + file.FullName + "' />")
                    .Deserialize<FileSetting>());

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.Path).Returns("/example.txt").Verifiable();
                mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
                mock.Setup(x => x.Response.Cache.SetLastModified(It.Is<DateTime>(date => date == file.LastWriteTimeUtc))).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FileStreamResult result = new FileController().Representation() as FileStreamResult;

                Assert.NotNull(result);

                mock.VerifyAll();
            }
            finally
            {
                ConfigurationManager.RefreshSection("simplewebservices/files");
                HttpContextFactory.Reset();
            }
        }
    }
}